package components.map.path
{
	import capricornus.core.StageManager;
	
	import components.map.MapView;
	import components.map.grid.MapGridController;
	import components.map.utils.EditType;
	import components.map.utils.PathType;
	
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.display.Sprite;
	import flash.events.MouseEvent;
	import flash.geom.ColorTransform;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.utils.ByteArray;
	
	import mx.containers.Grid;
	import mx.core.mx_internal;

	public class MapPathController
	{
		private var _view:MapView;
		private var _dragLayer:Sprite;
		private var _layer:Sprite;
		
		private var _gridController:MapGridController;
		
		private var _bdAlpha:BitmapData;
		private var _bdCanNotMove:BitmapData;
		private var _bdReadOnly:BitmapData;
		
		private var _bdBorder:BitmapData;
		private var _borderContainer:Sprite;
		private var _pen:Sprite;
		
		private var _cfRed:ColorTransform;
		private var _cfBlue:ColorTransform;
		private var _cfGray:ColorTransform;
		
		private var _arrData:Array;
		
		private var _bitmap:Bitmap;
		private var _bitmapData:BitmapData;
		
		private var _searcher:ActionSceneDataSearcher;
		
		public function MapPathController( view:MapView, gridController:MapGridController )
		{
			_view = view;
			_layer = view.backLayer;
			_dragLayer = _view.dragLayer;
			_gridController = gridController;
			init();
		}
		
		private function init():void
		{
			_layer.mouseChildren = false;
			_layer.mouseEnabled = false;
			_layer.cacheAsBitmap = true;
			
			_arrData = new Array();
			_bitmap = new Bitmap();
			_layer.addChild( _bitmap );
			
			_cfRed = new ColorTransform( 0, 0, 0, 1, 0xff );
			_cfBlue = new ColorTransform( 0, 0, 0, 1, 0, 0, 0xff );
			_cfGray = new ColorTransform();
			
			_bdAlpha = new AlphaCellPanel();
			_bdCanNotMove = new CanNotMoveCellPanel();
			_bdReadOnly = new ReadOnlyCellPanel();
			
			_bdBorder = new GridBorderPanel();
			
			_pen = new Sprite();
			_borderContainer = new Sprite();
			_borderContainer.cacheAsBitmap = true;
			_pen.addChild( _borderContainer );
			_pen.mouseChildren = false;
			_pen.mouseEnabled = false;
			_pen.visible = false;
			_pen.cacheAsBitmap = true;
			_pen.alpha = 0.5;
			_dragLayer.cacheAsBitmap = true;
			_dragLayer.addChild( _pen );
		}
		
		public function clear():void
		{
			_arrData.length = 0;
			_arrData = new Array();
			if( _searcher != null ) _searcher.clear();
			if( _bitmapData != null )
			{
				_bitmapData.dispose();
				_bitmapData = null;
			}
		}
		
		private var _enabled:Boolean = false;
		public function set enabled( value:Boolean ):void
		{
			if( _enabled != value )
			{
				_pen.visible = !_enabled;
				_enabled = value;
				if( _enabled )
				{
					_view.addEventListener( MouseEvent.MOUSE_DOWN, onMouseDown, false, 0, true );
					_view.addEventListener( MouseEvent.MOUSE_MOVE, onMouseMove, false, 0, true );
					_view.addEventListener( MouseEvent.MOUSE_UP, onMouseUp, false, 0, true );
				}
				else
				{
					_view.removeEventListener( MouseEvent.MOUSE_DOWN, onMouseDown );
					_view.removeEventListener( MouseEvent.MOUSE_MOVE, onMouseMove );
					_view.removeEventListener( MouseEvent.MOUSE_UP, onMouseUp );
				}
			}
		}
		
		private var _editType:int;
		public function set editType( value:int ):void
		{
			_editType = value;
			penSize = _penSize;
			if( _editType==EditType.ALPHA )
			{
				_borderContainer.transform.colorTransform = _cfBlue;
			}
			else if( _editType==EditType.CAN_NOT_MOVE )
			{
				_borderContainer.transform.colorTransform = _cfRed;
			}
			else
			{
				_borderContainer.transform.colorTransform = _cfGray;
			}
		}
		
		public function reset():void
		{
			_searcher = new ActionSceneDataSearcher( _arrData, _gridController.gridRowCount, _gridController.gridRowCount, PathType.CAN_NOT_MOVE ); 
		}
		
		public function getSearchPath( arrBegin:Array, arrEnd:Array ):Array
		{
			return _searcher.search( arrBegin[0], arrBegin[1], arrEnd[0], arrEnd[1] );
		}
		
		public function getSearchPath2( arrBegin:Array, arrEnd:Array ):Array
		{
			var path:Array = _searcher.search( arrBegin[0], arrBegin[1], arrEnd[0], arrEnd[1] );
			if (path != null) 
			{
				optimizationPath(path);
				
				path[0] = arrBegin[0];
				path[1] = arrBegin[1];
				var length:int = path.length-2;
				/*var c:int = _gridController.gridRowCount;
				for (var i:int = 2; i<length; i+=2) 
				{
					var row:int = path[i];
					var column:int = path[i+1];
					path[i] = (1+row+column)*20;
					path[i+1] = (c+row-column)*10;
				}*/
				path[length] = arrEnd[0];
				path[length+1] = arrEnd[1];
			}
			
			if (path.length == 4) {
				if (path[0] == path[2] && path[1] == path[3]) return null;
			}
			
			return path;
		}
		
		private function optimizationPath(path:Array):Array 
		{
			var length:int = path.length;
			var c:int = _gridController.gridRowCount;
			for (var i:int = 0; i<length; i+=2) 
			{
				var currentRow:Number = path[i]+0.5;
				var currentColumn:Number = path[i+1]+0.5;
				tag:for (var j:int = i+4; j<length; j+=2) 
				{
					var targetRow:Number = path[j]+0.5;
					var targetColumn:Number = path[j+1]+0.5;
					var x:Number = targetColumn-currentColumn;
					var y:Number = targetRow-currentRow;
					var totalDistance:Number = Math.sqrt(x*x+y*y);
					var angle:Number = Math.atan2(y, x);
					var cos:Number = Math.cos(angle);
					var sin:Number = Math.sin(angle);
					var k:Number = x == 0 || y == 0 ? 1 : 0.3;
					var currentDistance:Number = k;
					while (currentDistance<totalDistance) 
					{
						var row:Number = currentRow+sin*currentDistance;
						var column:Number = currentColumn+cos*currentDistance;
						var value:int = _arrData[int(row)*c+int(column)];
						if (value == PathType.CAN_NOT_MOVE) continue tag;
						currentDistance += k;
					}
					var num:int = j-i-2;
					path.splice(i+2, num);
					length -= num;
					j -= num;
				}
			}
			return path;
		}
		
		public function setBorderReadOnly():void
		{
			var x:int;
			var y:int;
			var gw:int = _gridController.gridWidth;
			var gh:int = _gridController.gridHeight;
			var mw:int = _gridController.mapWidth;
			var mh:int = _gridController.mapHeight;
			var arr:Array;
			var n:int;
			
			for( x=2; x<mw; x += gw )
			{
				arr = _gridController.getGridFromXY( x, -1 );
				n = arr[0] * _gridController.gridRowCount + arr[1];
				arr = _gridController.getXYFromGrid( arr[0], arr[1] );
				copy( _bdReadOnly, arr );
				_arrData[n] = PathType.READ_ONLY;
			}
			for( x=2; x<mw; x += gw )
			{
				arr = _gridController.getGridFromXY( x, mh )
				n = arr[0] * _gridController.gridRowCount + arr[1];
				arr = _gridController.getXYFromGrid( arr[0], arr[1] );
				copy( _bdReadOnly, arr );
				_arrData[n] = PathType.READ_ONLY;
			}
			for( y=1; y<mh; y += gh )
			{
				arr = _gridController.getGridFromXY( -1, y );
				n = arr[0] * _gridController.gridRowCount + arr[1];
				arr = _gridController.getXYFromGrid( arr[0], arr[1] );
				copy( _bdReadOnly, arr );
				_arrData[n] = PathType.READ_ONLY;
			}
			for( y=1; y<mh; y += gh )
			{
				arr = _gridController.getGridFromXY( mw+1, y );
				n = arr[0] * _gridController.gridRowCount + arr[1];
				arr = _gridController.getXYFromGrid( arr[0], arr[1] );
				copy( _bdReadOnly, arr );
				_arrData[n] = PathType.READ_ONLY;
			}
		}
		
		public function resetPen():void
		{
			penSize = _penSize;
		}
		
		public function getPathData():ByteArray
		{
			var byte:ByteArray = new ByteArray();
			var len:int = _gridController.gridRowCount;
			var arr:Array;
			for( var i:int=0; i<len; i++ )
			{
				for( var j:int=0; j<len; j++ )
				{
					var n:int = i * len + j;
					var data:int = int(_arrData[n]);
					if( data==PathType.MOVE )
					{
						arr = _gridController.getXYFromGrid( i, j );
						if( arr[0] < 0 || arr[1] < 0 || arr[0] + _gridController.gridWidth/2+1 > _gridController.mapWidth || arr[1] + _gridController.gridHeight/2+1 > _gridController.mapHeight )
						{
							byte.writeByte( PathType.CAN_NOT_MOVE );
						}
						else
						{
							byte.writeByte( PathType.MOVE );
						}
					}
					else
					{
						if( data==PathType.ALPHA )
						{
							byte.writeByte( PathType.ALPHA );
						}
						else
						{
							byte.writeByte( PathType.CAN_NOT_MOVE );
						}
					}
				}
			}
			return byte;
		}
		
		public function setPathData( byte:ByteArray ):void
		{
			var len:int = _gridController.gridRowCount;
			var arr:Array;
			for( var i:int=0; i<len; i++ )
			{
				for( var j:int=0; j<len; j++ )
				{
					var n:int = i * len + j;
					var data:int = int(_arrData[n]);
					arr = _gridController.getXYFromGrid( i, j );
					if( arr[0] < 0 || arr[1] < 0 || arr[0] + _gridController.gridWidth/2+1 > _gridController.mapWidth || arr[1] + _gridController.gridHeight/2+1 > _gridController.mapHeight )
					{
						//byte.writeByte( PathType.CAN_NOT_MOVE );
					}
					else
					{
						var type:int = byte[n];
						var bd:BitmapData = null;
						_arrData[n] = type;
						if( type==PathType.ALPHA )
						{
							bd = _bdAlpha;
						}
						else if( type==PathType.CAN_NOT_MOVE )
						{
							bd = _bdCanNotMove;
						}
						if( bd != null )
						{
							copy( bd, arr );
						}
					}
				}
			}
		}
		
		private var _penSize:int = 1;
		public function set penSize( value:int ):void
		{
			_penSize = value;
			while( _borderContainer.numChildren > 0 )
			{
				(_borderContainer.removeChildAt(0) as Bitmap).bitmapData = null;
			}
			var left:int;
			var n:int = _isRhombus?1:_penSize; 
			var a:int = 1;
			var i:int;
			var j:int;
			var bm:Bitmap;
			if( _isRhombus )
			{
				var h:int = 0;
				
				while( n > 0 )
				{
					left = -_gridController.gridWidth * (n-1)/2;
					for( j=0; j<n; j++ )
					{
						bm = new Bitmap( _bdBorder );
						_borderContainer.addChild( bm );
						bm.x = left + _gridController.gridWidth * j;
						bm.y = h;
					}
					h += _gridController.gridHeight/2;
					n += a;
					if( n>=_penSize )
					{
						a = -1;
					}
					if( _penSize==1 ) break;
				}
			}
			else
			{
				left = -_gridController.gridWidth * _penSize / 2
				for( i=0; i<_penSize; i++ )
				{
					for( j=0; j<_penSize; j++ )
					{
						bm = new Bitmap( _bdBorder );
						_borderContainer.addChild( bm );
						bm.x = left + _gridController.gridWidth * (_penSize - i);
						bm.y = j * _gridController.gridHeight;
					}
					if( i < _penSize -1 )
					{
						left += _gridController.gridWidth / 2;
						for( j=0; j<_penSize-1; j++ )
						{
							bm = new Bitmap( _bdBorder );
							_borderContainer.addChild( bm );
							bm.x = left + _gridController.gridWidth * (_penSize - i - 1);
							bm.y = j * _gridController.gridHeight + _gridController.gridHeight/2;
						}
						left -= _gridController.gridWidth / 2;
					}
				}
			}
			var rect:Rectangle = _borderContainer.getRect( _borderContainer );
			_borderContainer.x = -int(rect.x + _borderContainer.width/2);
			_borderContainer.y = -int(rect.y + _borderContainer.height/2);
		}
		
		private var _isRhombus:Boolean = true;
		public function set isRhombus( value:Boolean ):void
		{
			if( _isRhombus != value )
			{
				_isRhombus = value;
				penSize = _penSize;
			}
		}
		
		private function get bitmapData():BitmapData
		{
			if( _bitmapData==null )
			{
				_bitmapData = new BitmapData( _gridController.mapWidth, _gridController.mapHeight, true, 0 );
				_bitmap.bitmapData = _bitmapData;
			}
			return _bitmapData;
		}
		
		private var _isMouseDown:Boolean = false;
		private function onMouseDown( e:MouseEvent ):void
		{
			var pt:Point = new Point( e.stageX, e.stageY );
			pt = _layer.globalToLocal( pt );
			if( pt.x < 0 || pt.y < 0 || pt.x > _gridController.mapWidth || pt.y > _gridController.mapHeight )
			{
				return;
			}
			var len:int = _borderContainer.numChildren;
			var p:Point = new Point(_gridController.gridWidth/2, _gridController.gridHeight/2);
			for( var i:int=0; i<len; i++ )
			{
				var dis:DisplayObject = _borderContainer.getChildAt(i);
				pt = dis.localToGlobal( p );
				pt = _layer.globalToLocal( pt );
				
				var arr:Array = _gridController.getGridFromXY( pt.x, pt.y );
				var n:int = arr[0] * _gridController.gridRowCount + arr[1];
				arr = _gridController.getXYFromGrid( arr[0], arr[1] );
				if( arr[0] < 0 || arr[1] < 0 || arr[0] + _gridController.gridWidth/2+1 > _gridController.mapWidth || arr[1] + _gridController.gridHeight/2+1 > _gridController.mapHeight )
				{
					continue;
				}
				
				if( _editType==EditType.DEL_PATH )
				{
					_arrData[n] = PathType.MOVE;
					copy2( _bdAlpha, arr );
				}
				else
				{
					_arrData[n] = (_editType==EditType.ALPHA?PathType.ALPHA:PathType.CAN_NOT_MOVE);
					copy( (_editType==EditType.ALPHA?_bdAlpha:_bdCanNotMove), arr );
				}
			}
			
			_isMouseDown = true;
		}
		
		private function onMouseMove( e:MouseEvent ):void
		{
			if( _isMouseDown )
			{
				onMouseDown( e );
			}
			var pt:Point = new Point( e.stageX, e.stageY );
			pt = _layer.globalToLocal( pt );
			var arr:Array;
			if( _isRhombus )
			{
				arr = _gridController.getGridXYFromXY( pt.x, pt.y );
				if( _penSize % 2==1 )
				{
					_pen.x = int(arr[0] + _gridController.gridWidth/2);
					_pen.y = int(arr[1] + _gridController.gridHeight/2);
				}
				else
				{
					_pen.x = int(arr[0] + _gridController.gridWidth/2);
					_pen.y = int(arr[1]);
				}
			}
			else
			{
				arr = _gridController.getGridXYFromXY( pt.x, pt.y );
				_pen.x = int(arr[0] + _gridController.gridWidth/2);
				_pen.y = int(arr[1] + _gridController.gridHeight/2);
			}
		}
		
		private function onMouseUp( e:MouseEvent ):void
		{
			_isMouseDown = false;
		}
		
		private function copy( bd:BitmapData, arr:Array ):void
		{
			var w:int = bd.width;
			var h:int = bd.height;
			var x:int = arr[0];
			var y:int = arr[1];
			for( var i:int=0; i<w; i++ )
			{
				for( var j:int=0; j<h; j++ )
				{
					var color:uint = bd.getPixel32(i, j); 
					if( uint(color & 0xff000000) == 0xff000000 )
					{
						bitmapData.setPixel32( x+i, y+j, color ); 
					}
				}
			}
		}
		
		private function copy2( bd:BitmapData, arr:Array ):void
		{
			var w:int = bd.width;
			var h:int = bd.height;
			var x:int = arr[0];
			var y:int = arr[1];
			for( var i:int=0; i<w; i++ )
			{
				for( var j:int=0; j<h; j++ )
				{
					var color:uint = bd.getPixel32(i, j); 
					if( uint(color & 0xff000000) == 0xff000000 )
					{
						bitmapData.setPixel32( x+i, y+j, 0x00000000 ); 
					}
				}
			}
		}
	}
}

















